package chat

import (
	"container/heap"
	"fmt"
	"sync"
)

var _ heap.Interface = (*Heap)(nil)

const (
	allocSize = 4096
)

//Item heap item
type Item struct {
	idx   int
	ID    string
	Value int
}

type ItemSort []*Item

func (h ItemSort) Len() int           { return len(h) }
func (h ItemSort) Less(i, j int) bool { return (h[i]).Value < (h[j]).Value }
func (h ItemSort) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }

//Heap score heap
type Heap struct {
	data  []*Item
	index map[string]int //id -> data[index]
}

//NewHeap new heap
func NewHeap() *Heap {
	h := &Heap{
		data:  make([]*Item, 0, allocSize),
		index: make(map[string]int, allocSize),
	}
	return h
}

func (h *Heap) Len() int { return len(h.data) }
func (h *Heap) Less(i, j int) bool {
	vi := h.data[i].Value
	vj := h.data[j].Value
	if vi > vj {
		return true
	}
	return false
}

func (h *Heap) Swap(i, j int) {

	//update index
	h.index[h.data[i].ID], h.index[h.data[j].ID] = j, i

	//swap data
	h.data[i], h.data[j] = h.data[j], h.data[i]
	h.data[i].idx = i
	h.data[j].idx = j
}

//Push push element
func (h *Heap) Push(x interface{}) {
	n := len(h.data)
	item := x.(*Item)
	item.idx = n
	h.data = append(h.data, item)
	h.index[item.ID] = n
}

//Pop pop element
func (h *Heap) Pop() interface{} {
	n := len(h.data)
	if n == 0 {
		return nil
	}
	item := h.data[n-1]
	item.idx = -1
	h.data = h.data[:n-1]
	delete(h.index, item.ID)
	return item
}

//TopRank rank with score desc
type TopRank struct {
	sync.Mutex
	*Heap
	maxQuery int //最大查询数
}

//NewTopRank new score rank
func NewTopRank() *TopRank {
	sr := &TopRank{
		Heap: NewHeap(),
	}
	heap.Init(sr)
	return sr
}

//NewTopRankWithItem score rank with items
func NewTopRankWithItem(its []*Item) *TopRank {
	sr := &TopRank{
		Heap: NewHeap(),
	}
	heap.Init(sr)
	for _, v := range its {
		heap.Push(sr, v)
	}
	return sr
}

//SetMaxQuery 设查最大查询数
func (s *TopRank) SetMaxQuery(n int) error {
	s.maxQuery = n
	return nil
}

//Remove remove the data drom heap by id
func (s *TopRank) Remove(id string) interface{} {
	s.Lock()
	defer s.Unlock()
	idx, ok := s.index[id]
	if !ok {
		return nil
	}
	item := heap.Remove(s, idx)
	delete(s.index, id)
	return item
}

//Update update item Score in heap
func (s *TopRank) Update(it *Item) {
	s.Lock()
	defer s.Unlock()
	idx, ok := s.index[it.ID]
	if !ok {
		s.Push(it)
		return
	}
	s.data[idx].Value += it.Value
	heap.Fix(s, idx)

}

//Range get the range of (a, b) ranking
func (s *TopRank) Range(a, b int) []*Item {
	s.Lock()
	defer s.Unlock()
	// if a < 1 || b > len(s.data) {
	// 	return nil
	// }
	if a < 1 {
		a = 1
	}
	if b > len(s.data) {
		b = len(s.data)
	}
	items := make([]*Item, b-a+1)
	copy(items, s.data[a-1:b])

	return items
}

func (s *TopRank) String() string {
	str := "TopRank:\n"
	for _, v := range s.data {
		str += fmt.Sprintf("id:%v, val:%v, idx:%v --- index:%v\n", v.ID, v.Value, v.idx, s.index[v.ID])
	}
	return str
}
